// Copyright (c) Appeon. All rights reserved.
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. 
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;

namespace SnapObjects.Expressions
{
    public abstract class MetadataCollection<T>
    {
        protected readonly IDictionary<int, Metadata<T>> _groups;
        protected readonly IDictionary<string, Metadata<T>> _methods;
        protected readonly IDictionary<object, Metadata<T>> _variables;
        protected readonly IDictionary<object, Metadata<T>> _parameters;
        protected readonly ConcurrentBag<IMetadataProvider<T>> _providers;

        public MetadataCollection()
        {
            _groups = new ConcurrentDictionary<int, Metadata<T>>();
            _providers = new ConcurrentBag<IMetadataProvider<T>>();
            _variables = new ConcurrentDictionary<object, Metadata<T>>();
            _parameters = new ConcurrentDictionary<object, Metadata<T>>();
            _methods = new ConcurrentDictionary<string, Metadata<T>>(StringComparer.OrdinalIgnoreCase);
        }

        internal virtual bool TryGetMetadata(
            MetadataType type, object key, ParserOptions<T> options, out Metadata<T> metadata)
        {
            var providers = _providers.Where(
                x => x.Type == type && (options == null || x.SupportKinds.Contains(options.SourceKind)));

            foreach (var provider in providers)
            {
                if (provider.TryGet(key, options, out metadata))
                {
                    return true;
                }
            }

            switch (type)
            {
                case MetadataType.Group: return _groups.TryGetValue((int)key, out metadata);
                case MetadataType.Variable: return _variables.TryGetValue(key, out metadata);
                case MetadataType.Parameter: return _parameters.TryGetValue(key, out metadata);
                case MetadataType.Method: return _methods.TryGetValue((string)key, out metadata);
            }

            metadata = null;

            return false;
        }

        public abstract bool AddMethod<TClass>() where TClass : class;
        public abstract bool AddMethod<TClass>(TClass instance) where TClass : class;
        public abstract bool AddMethod(string key, Delegate method);
        public abstract bool AddParameter(string key, Type type);
        public abstract bool AddVariable(string key, object value);
        public abstract bool AddGroup(string key, IEnumerable<string> conditions);
        public abstract bool AddGroup(string key, Func<object, object> keySelector);

        public virtual bool AddMetadata(Metadata<T> metadata)
        {
            switch (metadata.Type)
            {
                case MetadataType.Group:
                    break;
                case MetadataType.Method:
                    break;
                case MetadataType.Property:
                    break;
                case MetadataType.Variable:
                    break;
                case MetadataType.Parameter:
                    break;
                default:
                    break;
            }

            return true;
        }

        public virtual bool AddMetadataProvider(IMetadataProvider<T> provider)
        {
            _providers.Add(provider);

            return true;
        }
    }
}
